home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / osr5 / sco / scripts / admin / chuser < prev    next >
Encoding:
Korn shell script  |  1997-08-26  |  8.3 KB  |  290 lines

  1. #!/bin/ksh
  2. # @(#) chuser.ksh 1.2.1 97/07/30
  3. # 94/01/22 john h. dubois iii (john@armory.com)
  4. # 94/09/09 Added check for non-matchable password.
  5. # 94/10/11 Use % instead of - as sed separator, to guarantee that it cannot
  6. #          appear in a user name.
  7. # 94/10/13 Added -u option.  Added sanity checks.
  8. # 94/10/22 Do not allow _ in login names.
  9. # 94/11/14 Fixed regexp used for checking if login name is in use.
  10. #          Fixed unsetting of old uid.
  11. # 95/10/28 Added t option.
  12. # 96/01/20 Create tempfile more carefully.
  13. # 97/07/30 Added f option.
  14.  
  15. # todo: when changing uid, check for processes, ipc objects, etc. owned by
  16. # the old uid and warn about them.
  17.  
  18. # @(#) mktempfile 1.0 96/05/22
  19. # 96/01/20 jhdiii
  20.  
  21. # Usage: mkfiles name ...
  22. # Creates the named files with some attempt at security.
  23. # This will be more reliable if user do not have chown authority.
  24. # Any file that contains no / characters is created in the user's tempdir.
  25. # If TMP was not set, it is set by this function (but not exported).
  26. # Returns 0 on success; prints a diagnostic message & returns 1 on failure.
  27. function mkfiles {
  28.     typeset file files
  29.     typeset -i i=0
  30.  
  31.     : ${TMP:=$TMPDIR}
  32.     : ${TMP:=/tmp}
  33.     for file; do
  34.     [[ "$file" != */* ]] && file="$TMP/$file"
  35.     files[i]=$file
  36.     let i+=1
  37.     done
  38.     set -- "${files[@]}"
  39.  
  40.     rm -f "$@" || {
  41.     # hopefully rm will have printed a more specific message.
  42.     print -u2 "Could not remove pre-existing files."
  43.     return 1
  44.     }
  45.     for file; do
  46.     # Use >> to avoid 0'ing the file in case a symlink was just made from
  47.     # the filename to something we don't want to truncate
  48.     >> "$file" || {
  49.         print -u2 "Could not create temp file $file"
  50.         return 1
  51.     }
  52.     # These are very suspicious
  53.     [ -s "$file" ] && {
  54.         print -u2 "New tempfile is not empty!!!"
  55.         return 1
  56.     }
  57.     [ -O "$file" ] || {
  58.         print -u2 "Do not own $file!!!"
  59.         return 1
  60.     }
  61.     done
  62.     # Could do some more stuff here, but anyone concerned with security should
  63.     # have chown authorization off for most users.
  64.     return 0
  65. }
  66.  
  67. # Usage: mktempfile name
  68. # Creates a tempfile name tempdir/#name$$, and sets the global mktempfile_ret
  69. # to that name.  tempdir is a temp directory in $TMP, $TMPDIR, or /tmp, and $$
  70. # is the PID of the current process.
  71. # name should be 8 characters or less, so that the resulting filename will
  72. # not be more than 14 characters long (a limit on some machines).
  73. # Returns 0 on success, prints a diagnostic message & returns 1 on failure.
  74. function mktempfile {
  75.     typeset file="#$1$$"
  76.     mkfiles "$file"
  77.     mktempfile_ret="$TMP/#$1$$"
  78. }
  79.  
  80. # Usage: Error [-f] message ...
  81. # If -f is given, this is considered a fatal error even if tell is true
  82. function Error {
  83.     typeset warn
  84.  
  85.     if [ $# -gt 1 -a "$1" = -f ]; then
  86.     warn=false
  87.     shift
  88.     else
  89.     warn=$tell
  90.     fi
  91.     if $warn; then
  92.     print -u2 "$name: Warning: $* (continuing)."
  93.     else
  94.     print -u2 "$name: Fatal error: $*.  Aborting."
  95.     exit 1
  96.     fi
  97. }
  98.  
  99. ### Start of main program
  100.  
  101. name=${0##*/}
  102. unset newuid
  103. tell=false
  104. chkname=true
  105.  
  106. Usage="Usage: One of the following forms:
  107.   $name -h
  108.   $name [-ft] oldname newname
  109.   $name [-ft] -u<uid> oldname [newname]"
  110.  
  111. while getopts :hftu: opt; do
  112.     case $opt in
  113.     h)
  114.     print \
  115. "$name: change a user's account name or uid.
  116. $Usage
  117. Either -u<uid> or [newname] or both may be given.  If -u<uid> is given, the
  118. user's old uid is changed to <uid>.  If newname is given, the user's login name
  119. is changed from oldname to newname and the user's home directory is renamed
  120. ($name assumes that the last component of the login directory is the same as
  121. the user name).  In both cases, the user's protected password database
  122. information is updated.  $name will fail if the named user is logged in, or if
  123. newname or the new uid is already in use.
  124. Other options:
  125. -f: Force change even if newname is not a legal user name (for example, if
  126.     newname starts with a digit).  Be very careful when using this option.
  127. -h: Print this help.
  128. -t: Tell what would be done, without doing it.  This changes some fatal errors
  129.     to warnings."
  130.     exit 0
  131.     ;;
  132.     f)
  133.     chkname=false
  134.     ;;
  135.     t)
  136.     tell=true
  137.     ;;
  138.     u)
  139.     newuid=$OPTARG
  140.     if [[ "$newuid" != +([0-9]) || 
  141.     "$newuid" -lt 1 || "$newuid" -gt 32767 ]]; then
  142.         Error -f "Bad value given for new uid"
  143.     fi
  144.     grep "^[^:]*:[^:]*:$newuid:" /etc/passwd > /dev/null &&
  145.     Error "new uid '$newuid' is already in use"
  146.     ;;
  147.     +?)
  148.     Error -f "options should not be preceded by a '+'"
  149.     ;;
  150.     :)
  151.         Error -f "Option '$OPTARG' requires a value.  Use -h for help."
  152.         ;;
  153.     ?) 
  154.     Error -f "$OPTARG: bad option.  Use -h for help"
  155.     ;;
  156.     esac
  157. done
  158.  
  159. # remove args that were options
  160. let OPTIND=OPTIND-1
  161. shift $OPTIND
  162.  
  163. # Must have 1 or 2 args; if only 1 arg, must have given -u option.
  164. if [ $# -ne 1 -a $# -ne 2 -o $# -eq 1 -a -z "$newuid" ]; then
  165.     Error -f "Not enough arguments.  Use -h for help.\n"
  166. fi
  167.  
  168. oldname=$1
  169. if [ $# -eq 1 ]; then
  170.     newname=$1    # replace it with itself
  171. else
  172.     newname=$2
  173.     egrep "^$newname:" /etc/passwd > /dev/null &&
  174.     Error "$newname is already a user"
  175.     # getty man page says _ should not be used in a login name,
  176.     # and every other non-alphanum except - is used as a metachar
  177.     # somewhere where a login name would be used.
  178.     if $chkname &&
  179.     [[ "$newname" != [a-z]+([-a-z0-9]) || "$newname" = ?????????* ]]; then
  180.     Error "$newname: invalid user name"
  181.     fi
  182. fi
  183.  
  184. # Get olduid even if -u not given, since it is used as a check for whether
  185. # a good username was given.
  186. olduid=$(awk -F: "/^$oldname:/ { print \$3;}" /etc/passwd)
  187.  
  188. [ -z "$olduid" ] && Error -f "$oldname is not a user"
  189.  
  190. # If not changing uid, unset olduid, lest old uid be replaced with nothing
  191. [ -z "$newuid" ] && unset olduid
  192.  
  193. # At this point:
  194. # oldname is the name of the user whose account is being changed.
  195. # newname is either the new name for the account, or is the same as oldname
  196. #     if the name is not being changed.
  197. # If the uid is being changed, olduid is the old uid and newuid is the new uid.
  198. # If the uid isn't being changed, they are both null.
  199.  
  200. if who | egrep "^$oldname "; then
  201.     Error "$oldname is logged in; cannot change account record"
  202. fi
  203.  
  204. # ap output looks like this:
  205. # stealth:x:1032:50:Jonathan D. Campbell:/u/stealth:/bin/ksh
  206. # stealth:u_name=stealth:u_id#1032:\
  207. #         :u_pwd=x:\
  208. #         :u_type=general:u_lock@:chkent:
  209. # ENDOFGROUPS::0:
  210.  
  211. # Save user account configuration, translated to new name
  212. # Replace user name at start of line followed by a colon,
  213. # and user name after an = or / followed by colon (u_name and home dir fields)
  214. mktempfile chuser. || {
  215.     print -u2 "$name: Exiting."
  216.     exit 1
  217. }
  218. tmpfile=$mktempfile_ret
  219.  
  220. print "Saving account profile for $newname in $tmpfile"
  221. # Line 1 subs passwd and tcb login name.
  222. # Line 2 subs tcb u_name.
  223. # Line 3 subs passwd directory name.
  224. # Line 4 subs passwd uid.
  225. # Line 5 subs passwd uid.
  226. ap -gd "$oldname" | sed "
  227. s/^$oldname:/$newname:/
  228. s/=$oldname:/=$newname:/
  229. s%/$oldname:%/$newname:%
  230. s/:$olduid:/:$newuid:/
  231. s/#$olduid:/#$newuid:/
  232. " > $tmpfile
  233.  
  234. grep :u_pwd=x: $tmpfile && grep "^$newname:x:" $tmpfile &&
  235. print -u2 "$name: Warning: account profile contains non-matchable password."
  236.  
  237. uhome=`awk -F: "\"$oldname"'" == $1 { print $6; }' /etc/passwd`
  238.  
  239. if $tell; then
  240.     print "Would remove user $oldname."
  241. else
  242.     print "Removing user $oldname..."
  243.     rmuser "$oldname"
  244.     status=$?
  245.     if [ $status -ne 0 ]; then
  246.     # Do NOT remove tmpfile; rmuser may exit nonzero even if user is
  247.     # mostly removed!
  248.     #rm $tmpfile
  249.     Error -f \
  250.     "Removal of user $oldname failed (rmuser exited with status $status)"
  251.     fi
  252. fi
  253.  
  254. if $tell; then
  255.     print "Would restore user $newname with account profile:"
  256.     cat $tmpfile
  257. else
  258.     print "Restoring user $newname with account profile:"
  259.     cat $tmpfile
  260.     ap -r -f $tmpfile
  261. fi
  262.  
  263. if [ $# -eq 2 ]; then
  264.     # Replace /oldname$ with /newname
  265.     newhome=`print -r -- $uhome|sed "s/\\/$oldname\\$/\\/$newname/"`
  266.  
  267.     if $tell; then
  268.     print -r "Would move home dir $uhome to $newhome"
  269.     else
  270.     print -r "Moving home dir $uhome to $newhome"
  271.     fi
  272.     if [ -d $uhome ]; then
  273.     $tell || mvdir $uhome $newhome
  274.     else
  275.     print -u2 \
  276.     "$uhome is not a directory!  If it is a symlink, move it by hand."
  277.     fi
  278. fi
  279.  
  280. [ -n "$newuid" ] && print \
  281. "Remember to change the ownership of any files
  282. from $olduid to $newuid ($newname)."
  283.  
  284. [ "$oldname" != "$newname" ] && print \
  285. "Remember to create a mail alias, if appropriate, to forward mail
  286. from $oldname to $newname.  Here's an alias line to cut & paste:
  287. $oldname    $newname"
  288.  
  289. rm $tmpfile
  290.